using Gtk;
using Cairo;

namespace Ribbons {

	public class ApplicationMenu : Container {

		private const double LINE_WIDTH = 1.0;
		private const int TOP_PADDING = 24;
		private const int BORDER_WIDTH = 6;
		private const int SPACE = 2;
		private const int VERTICAL_WINDOW_OFFSET = TOP_PADDING - SPACE;

		protected Theme _theme = new Theme ();

		private Gee.List<ApplicationMenuItem> _items;
		private Widget _default_menu;
		private int _item_height;
		private Gdk.Size _menu_size;

		private Widget _active_menu;

		private Gdk.Rectangle _items_alloc;
		private int _menu_items_col_width;
		private int _buttons_height;
		private int _exit_button_width;
		private int _options_button_width;
		private int _visible_menu_items;
//		private bool _active_menu_visible;		// XXX-GEETK: never used
		private bool _options_button_visible;
		private bool _exit_button_visible;

		private Window _win;

		public ApplicationButton application_button { get; construct; }

		private Button _options_button;
		public Button options_button {
			get { return _options_button; }
			set {
				if (_options_button == value) {
					return;
				}
				if (_options_button != null) {
					_options_button.unparent ();
				}
				_options_button = value;
				if (value != null) {
					value.draw_background = true;
					value.opaque_background = true;
					value.set_parent (this);
					value.visible = true;
				}
			}
		}

		private Button _exit_button;
		public Button exit_button {
			get { return _exit_button; }
			set {
				if (_exit_button == value) {
					return;
				}
				if (_exit_button != null) {
					_exit_button.unparent ();
				}
				_exit_button = value;
				if (value != null) {
					value.draw_background = true;
					value.opaque_background = true;
					value.set_parent (this);
					value.visible = true;
				}
			}
		}

		public int item_height {
			get { return _item_height; }
			set {
				if (_item_height == value) {
					return;
				}
				_item_height = value;
				queue_resize ();
			}
		}

		public Gdk.Size menu_size {
			get { return _menu_size; }
			set {
//				if (_menu_size == value) {		// XXX-GEETK
//					return;
//				}
				_menu_size = value;
				queue_resize ();
			}
		}

		public Widget default_menu {
			get { return _default_menu; }
			set {
				if (_default_menu == value) {
					return;
				}
				bool update_active = _default_menu == _active_menu;
				if (update_active && _default_menu != null) {
					_default_menu.unparent ();
				}
				_default_menu = value;
				if (update_active) {
					set_active_menu (value);
				}
			}
		}

		/** Returns the number of children. */
		public int n_children {
			get { return _items.size; }
		}

		/** Constructor. */
		public ApplicationMenu (ApplicationButton button) {
			this.application_button = button;
		}

		construct {
			set_flags (get_flags () | WidgetFlags.NO_WINDOW);

			add_events (Gdk.EventMask.BUTTON_PRESS_MASK
			          | Gdk.EventMask.BUTTON_RELEASE_MASK
			          | Gdk.EventMask.POINTER_MOTION_MASK);

//			this.children = new Gee.ArrayList<Widget> ();

//			append (new Button ("OK"));

			_items = new Gee.ArrayList<ApplicationMenuItem> ();
			_item_height = 32;
			_menu_size = Gdk.Size (240, 320);
		}

		public void prepend (ApplicationMenuItem item) {
			insert (item, 0);
		}

		public void append (ApplicationMenuItem item) {
			insert (item, -1);
		}

		public void insert (ApplicationMenuItem item, int index) {
			item.set_parent (this);
			item.visible = true;

			if (index == -1) {
				_items.add (item);
			} else {
				_items.insert (index, item);
			}
		}

		public void remove_item (int index) {		// XXX-GEETK name collision workaround
			if (index == -1) {
				index = _items.size - 1;
			}

			_items[index].unparent ();
			_items.remove_at (index);
		}

		public void activate_menu (Widget widget) {
			if (widget == null) {
				set_active_menu (_default_menu);
			} else {
				set_active_menu (widget);
			}
		}

		private void set_active_menu (Widget widget) {
			if (_active_menu != null) {
				_active_menu.unparent ();
			}
			_active_menu = widget;
			widget.set_parent (this);
			widget.visible = true;
			queue_resize ();
		}

		public void show_at (int x, int y) {
//			if (_win != null) {
//				kill_menu (true);
//			}

//			foreach (var item in _items) {
//				item.set_parent (this);
//			}

			if (_win == null) {
				_win = new SyntheticWindow (WindowType.POPUP);
				_win.add (this);

				_win.hide.connect (() => {
					kill_menu (true);
				});

				_win.show_all ();
				_win.window.move (x, y - VERTICAL_WINDOW_OFFSET);

				_win.button_press_event.connect (() => {
					kill_menu (true);
				});

				_win.add_events (Gdk.EventMask.BUTTON_PRESS_MASK
				               | Gdk.EventMask.BUTTON_RELEASE_MASK
				               | Gdk.EventMask.POINTER_MOTION_MASK);
			} else {
				_win.show_all ();
				_win.window.move (x, y - VERTICAL_WINDOW_OFFSET);
			}

			Gtk.grab_add (_win);
			Gdk.GrabStatus grabbed = Gdk.pointer_grab (_win.window, true,
			                                 Gdk.EventMask.BUTTON_PRESS_MASK,
			                                 null, null, 0);
			if (grabbed != Gdk.GrabStatus.SUCCESS) {
				kill_menu (false);
				return;
			}

			grabbed = Gdk.keyboard_grab (_win.window, true, 0);
			if (grabbed != Gdk.GrabStatus.SUCCESS) {
				kill_menu (false);
				return;
			}
		}

		private void kill_menu (bool ungrab) {
			if (_win == null) {
				return;
			}

//			Window win = _win;
//			_win = null;

//			win.child = null;

			Gtk.grab_remove (_win);
			if (ungrab) {
				Gdk.pointer_ungrab (0);
				Gdk.keyboard_ungrab (0);
			}
			_win.hide ();
//			_win.destroy ();
		}

		protected override void forall_internal (bool include_internals,
		                                         Gtk.Callback callback)
		{
			foreach (var widget in _items) {
				if (widget.visible) callback (widget);
			}

			if (_options_button != null && _options_button.visible) {
				callback (_options_button);
			}

			if (_exit_button != null && _exit_button.visible) {
				callback (_exit_button);
			}

			if (_active_menu != null && _active_menu.visible) {
				callback (_active_menu);
			}
		}

		protected override void size_request (out Requisition requisition) {
			base.size_request (out requisition);

			_menu_items_col_width = 0;
			int menu_items_col_height = 0;

			foreach (var mi in _items) {
				if (mi.visible) {
					mi.height_request = _item_height;
					Gtk.Requisition req;
					mi.size_request (out req);
					if (req.width > _menu_items_col_width) {
						_menu_items_col_width = req.width;
					}
					menu_items_col_height += _item_height;
				}
			}

			requisition.height = menu_items_col_height;
			requisition.width = _menu_items_col_width;

			if (_active_menu != null) {
//				Gtk.Requisition req;
//				_active_menu.size_request (out req);
//				requisition.width += req.width;
//				if (req.height > requisition.height) {
//					requisition.height = req.height;
//				}

				requisition.width += _menu_size.width;
				if (_menu_size.height > requisition.height) {
					requisition.height = _menu_size.height;
				}
			}

			int buttons_width = 0;
			_buttons_height = 0;

			if (_options_button != null) {
				Gtk.Requisition req;
				_options_button.size_request (out req);
				buttons_width = req.width;
				_buttons_height = req.height;
				_options_button_width = req.width;
			}

			if (_exit_button != null) {
				Gtk.Requisition req;
				_exit_button.size_request (out req);
				buttons_width += req.width;
				if (_options_button != null) {
					buttons_width += SPACE;
				}
				if (req.height > _buttons_height) {
					_buttons_height = req.height;
				}
				_exit_button_width = req.width;
			}

			if (buttons_width > requisition.width) {
				requisition.width = buttons_width;
			}

			if (_buttons_height > 0) {
				requisition.height += _buttons_height + SPACE;
			}
			requisition.width += BORDER_WIDTH << 1;
			requisition.height += BORDER_WIDTH + TOP_PADDING;

			this._menu_items_col_width = _menu_items_col_width;		// XXX-GEETK: strange line
		}

		protected override void size_allocate (Gdk.Rectangle a) {
			base.size_allocate (a);
			Gdk.Rectangle allocation = a;	// XXX-GEETK

			_visible_menu_items = 0;
			_exit_button_visible = _options_button_visible = false;

			allocation.height -= BORDER_WIDTH;

			if (_buttons_height + TOP_PADDING <= allocation.height) {
				Gdk.Rectangle alloc = Gdk.Rectangle ();

				if (_buttons_height > 0) {
					alloc.x = Gdk.Rect.right (allocation) - BORDER_WIDTH;
					alloc.y = Gdk.Rect.bottom (allocation) - _buttons_height;
					alloc.height = _buttons_height;

					if (_exit_button != null) {
						alloc.x -= _exit_button_width;
						alloc.width = _exit_button_width;
						if (alloc.x >= allocation.x + BORDER_WIDTH) {
							_exit_button.size_allocate (alloc);
						}
					}

					if (_options_button != null) {
						if (_exit_button != null) {
							alloc.x -= SPACE;
						}
						alloc.x -= _options_button_width;
						alloc.width = _options_button_width;
						if (alloc.x >= allocation.x + BORDER_WIDTH) {
							_options_button.size_allocate (alloc);
						}
					}

					allocation.height -= _buttons_height + SPACE;
				}

				alloc.x = allocation.x + BORDER_WIDTH;
				alloc.y = allocation.y + TOP_PADDING;
				_items_alloc.x = alloc.x;
				_items_alloc.y = alloc.y;
				alloc.height = _item_height;
				if (Gdk.Rect.right (allocation) - alloc.x - BORDER_WIDTH < _menu_items_col_width) {
					_menu_items_col_width = Gdk.Rect.right (allocation) - alloc.x - BORDER_WIDTH;
				}

				if (_menu_items_col_width > 0) {
					alloc.width = _menu_items_col_width;

					foreach (var mi in _items) {
						if (mi.visible) {
							if (Gdk.Rect.bottom (alloc) <= Gdk.Rect.bottom (allocation)) {
								mi.size_allocate (alloc);
								alloc.y += _item_height;
								_visible_menu_items++;
							}
						}
					}
				}

				_items_alloc.width = _menu_items_col_width + SPACE;
				_items_alloc.height = Gdk.Rect.bottom (allocation) - _items_alloc.y - BORDER_WIDTH;

				if (_active_menu != null) {
					alloc.x = allocation.x + BORDER_WIDTH + _menu_items_col_width + SPACE;
					alloc.width = Gdk.Rect.right (allocation) - alloc.x - BORDER_WIDTH;
					alloc.y = allocation.y + TOP_PADDING;
					alloc.height = Gdk.Rect.bottom (allocation) - alloc.y - BORDER_WIDTH;

					if (alloc.width > 0 && alloc.width > 0) {
						_active_menu.size_allocate (alloc);
					}
				}
			}
		}

		protected override bool expose_event (Gdk.EventExpose event) {
			var cr = Gdk.cairo_create (this.window);

			cr.rectangle (event.area.x, event.area.y,
			              event.area.width, event.area.height);
			cr.clip ();
			draw (cr);

//			cr.target.dispose ();		// XXX-GEETK
//			cr.dispose ();

			return base.expose_event (event);
		}

		protected void draw (Context cr) {
			Gdk.Rectangle rect = (Gdk.Rectangle) this.allocation;
			_theme.draw_application_menu (cr, rect, _items_alloc,
			                              LINE_WIDTH, this);
		}
	}
}
